package com.sinog.core.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @desc 时间工具类
 * @author -
 * @date -
 */
@Slf4j
public class DateUtil {

    private static final String PATTERN = "yyyy-MM-dd";
    private static final int MONTH_NUM = 12;
    /**
     * 重写initialValue
     */
    private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat(PATTERN));

    private DateUtil() {
        throw new IllegalStateException("unable to instance Utility class");
    }

    private static long toSeconds(long date) {
        return date / 1000L;
    }

    private static long toMinutes(long date) {
        return toSeconds(date) / 60L;
    }

    private static long toHours(long date) {
        return toMinutes(date) / 60L;
    }

    private static long toDays(long date) {
        return toHours(date) / 24L;
    }

    private static long toMonths(long date) {
        return toDays(date) / 30L;
    }

    private static long toYears(long date) {
        return toMonths(date) / 365L;
    }

    /**
     * 格式化成某格式的时间串
     * @param date date
     * @param format format
     * @return String
     */
    public static String returnStringTime(Date date,String format) {
        SimpleDateFormat formatter = new SimpleDateFormat(format);
        return formatter.format(date);
    }

    /**
     * 计算两个时间戳相差分钟数
     * @param date date
     * @return long
     */
    public static long getPastTime(long date) {
        long delta = System.currentTimeMillis() - date;
        return toMinutes(delta);
    }

    /**
     * 计算两个时间间隔天数
     * @param date1 date1
     * @param date2 date2
     * @return long
     */
    public static long getCalculationDays(long date1,long date2) {
        return  ((date1 - date2) / (1000 * 60 * 60 * 24));
    }

    /**
     * 根据日期获得年龄
     * @param birthDay birthDay
     * @return int
     */
    public static int getAge(Date birthDay) {
        if(null == birthDay) {
            throw new IllegalArgumentException("The birthDay is null.It's unbelievable!");
        }
        Calendar cal = Calendar.getInstance();
        if(cal.before(birthDay)) {
            throw new IllegalArgumentException("The birthDay is before Now.It's unbelievable!");
        }
        int yearNow = cal.get(Calendar.YEAR);
        int monthNow = cal.get(Calendar.MONTH);
        int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH);
        cal.setTime(birthDay);
        int yearBirth = cal.get(Calendar.YEAR);
        int monthBirth = cal.get(Calendar.MONTH);
        int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
        int age = yearNow - yearBirth;
        if(monthNow <= monthBirth) {
            if(monthNow == monthBirth) {
                if(dayOfMonthNow < dayOfMonthBirth) {
                    age--;
                }
            } else {
                age--;
            }
        }
        return age;
    }

    /**
     * 返回当前日期的前6个月
     * @param startDate startDate
     * @param endDate endDate
     * @return List<Date>
     */
    public static List<Date> sixMonthDate(Date startDate,Date endDate) {
        List<Date> listMonth = new ArrayList<>(16);
        Date now = new Date();
        Calendar c = Calendar.getInstance();
        //默认查询6个月数据
        int month = 6;
        //开始日期和结束日期都为null,当前日期往前推6个月
        if(null == startDate && null == endDate) {
            c.setTime(now);
            //开始日期为null,结束日期（当前日期往前推6个月）
        } else if(null == startDate) {
            //结束日期<=当前日期，结束日期往前推6个月
            if(endDate.getTime() <= now.getTime()) {
                c.setTime(endDate);
                // 当前日期往前推6个月
            } else {
                c.setTime(now);
            }
            //结束日期为null,开始日期 到 当前日期
        } else if(null == endDate) {
            month = difMonth(startDate,now);
            c.setTime(now);
            //开始日期和结束日期都不为null
        } else {
            month = difMonth(startDate,endDate);
            c.setTime(endDate);
        }
        for(int i = 0; i < month; i++) {
            listMonth.add(c.getTime());
            c.add(Calendar.MONTH,-1);
        }
        Collections.reverse(listMonth);
        return listMonth;
    }

    /**
     * 计算两个月得差值
     * @param startDate startDate
     * @param endDate endDate
     * @return Integer
     */
    private static Integer difMonth(Date startDate,Date endDate) {
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startDate);
        Calendar nowCal = Calendar.getInstance();
        nowCal.setTime(endDate);
        int nowYear = nowCal.get(Calendar.YEAR);
        int nowMonth = nowCal.get(Calendar.MONTH);
        int startYear = startCal.get(Calendar.YEAR);
        int startMonth = startCal.get(Calendar.MONTH);
        int month = nowYear * 12 + nowMonth - (startYear * 12 + startMonth);
        return month + 1;
    }

    /**
     * 校验字符串日期格式是否合法
     * @param arg arg
     * @return boolean
     */
    public static boolean checkStrDate(String arg) {
        if(StringUtils.isEmpty(arg)) {
            return true;
        }
        //日期格式，不带时间
        String pat1 = "\\d{4}-\\d{2}-\\d{2}";
        //日期时间格式
        String pat2 = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}";
        Pattern p1 = Pattern.compile(pat1);
        Matcher m1 = p1.matcher(arg);
        Pattern p2 = Pattern.compile(pat2);
        Matcher m2 = p2.matcher(arg);
        return !m1.matches() && !m2.matches();
    }

    /**
     * 获取当前日期的yyyy-MM-dd字符串格式
     * @return String
     */
    public static String getDate() {
        //设置日期格式
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        // new Date()为获取当前系统时间，也可使用当前时间戳
        return df.format(new Date());
    }

    /**
     * 获取过去12个月字符串数组
     * @return String[]
     */
    public static String[] getLatest12Month() {
        //获取当前日期(包含当前日期)
        String[] newMonths = new String[MONTH_NUM];
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.set(Calendar.MONTH,cal.get(Calendar.MONTH));
        // 加一行代码,否则3月重复
        cal.set(Calendar.DATE,1);
        for(int i = 0; i < MONTH_NUM; i++) {
            int month = cal.get(Calendar.MONTH) + 1;
            newMonths[MONTH_NUM - 1 - i] = cal.get(Calendar.YEAR) + "-" + (month < MONTH_NUM - 2 ? "0" + month : month);
            cal.set(Calendar.MONTH,cal.get(Calendar.MONTH) - 1);
        }
        return newMonths;
    }

    /**
     * 获取过去24个月字符串数组
     * @param date date
     * @return String[]
     */
    public static String[] getLatest24Month(String date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        Date parse = null;
        try {
            parse = sdf.parse(date);
        } catch(ParseException e) {
            log.error("发生异常:",e);
        }
        String[] months = new String[12];
        Calendar cal = Calendar.getInstance();
        cal.setTime(Objects.requireNonNull(parse));
        cal.set(Calendar.MONTH,cal.get(Calendar.MONTH));
        // 加一行代码,否则3月重复
        cal.set(Calendar.DATE,1);
        for(int i = 0; i < MONTH_NUM; i++) {
            int month = cal.get(Calendar.MONTH);
            months[11 - i] = cal.get(Calendar.YEAR) + "-" + (month < 10 ? "0" + month : month);
            cal.set(Calendar.MONTH,cal.get(Calendar.MONTH) - 1);
        }
        return months;
    }

    /**
     * 获取指定时间
     * @param num 传输正,负数、返回相应时间
     * @return String
     */
    public static String getSpecifiedTime(int num){
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        //在当前时间添加/减少num天，比如 20220101，num=5 结果：20220106
        calendar.add(Calendar.DAY_OF_YEAR, num);
        return THREAD_LOCAL.get().format(calendar.getTime());
    }

    /**
     * 时间工具——按天间隔
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @return 相隔天数集合
     * @throws ParseException ParseException
     */
    public static List<String> dateInterval(String startDate,String endDate) throws ParseException {
        List<String> dateInterval = new ArrayList<>(16);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long time = sdf.parse(startDate).getTime();
        long endTime = sdf.parse(endDate).getTime();
        long oneDay = 1000 * 60 * 60 * 24L;
        while(time <= endTime) {
            Date d = new Date(time);
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            dateInterval.add(df.format(d));
            time += oneDay;
        }
        return dateInterval;
    }

    /**
     * 时间工具-按月间隔
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 相隔月份集合
     */
    public static List<String> dateIntervalMonth(String startTime,String endTime) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        List<String> list = new ArrayList<>();
        try {
            Date startDate = sdf.parse(startTime);
            Date endDate = sdf.parse(endTime);
            Calendar calendar = Calendar.getInstance();
            while(startDate.getTime() <= endDate.getTime()) {
                // 把日期添加到集合
                list.add(sdf.format(startDate));
                // 设置日期
                calendar.setTime(startDate);
                //把日期增加一天
                calendar.add(Calendar.MONTH,1);
                // 获取增加后的日期
                startDate = calendar.getTime();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 将localDateTime转换为Date
     * @return Date
     */
    public static Date transferDate(LocalDateTime dateTime) {
        //通过ZoneId的systemDefault获取默认时区
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zonedDateTime = dateTime.atZone(zoneId);
        Instant instant = zonedDateTime.toInstant();
        return Date.from(instant);
    }

    /**
     * 根据一定的格式将毫秒级时间戳转为字符串类型的日期格式
     * @param timestamp 毫秒级时间戳
     * @param pattern 日期的格式化形式
     * @return String 一定格式的日期字符串
     */
    public static String chuoTransferDate(Long timestamp,String pattern) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
        return simpleDateFormat.format(new Date(timestamp));
    }
}